/**   
 * @Title: AutoMapperInterceptor.java 
 * @Package com.org.framework.mybatis.interceptor 
 * @Description: TODO(用一句话描述该文件做什么) 
 * @author cssuger@163.com   
 * @date 2017年1月23日 下午1:08:18 
 * @version V1.0   
 */
package com.bicon.base.data.common.mybatis.interceptor;

import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.List;
import java.util.Map;
import java.util.Properties;

import org.apache.commons.dbutils.DbUtils;
import org.apache.commons.lang3.exception.ExceptionUtils;
import org.apache.ibatis.builder.SqlSourceBuilder;
import org.apache.ibatis.executor.parameter.ParameterHandler;
import org.apache.ibatis.executor.statement.StatementHandler;
import org.apache.ibatis.mapping.BoundSql;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.mapping.ParameterMapping;
import org.apache.ibatis.mapping.SqlSource;
import org.apache.ibatis.plugin.Interceptor;
import org.apache.ibatis.plugin.Intercepts;
import org.apache.ibatis.plugin.Invocation;
import org.apache.ibatis.plugin.Plugin;
import org.apache.ibatis.plugin.Signature;
import org.apache.ibatis.reflection.MetaObject;
import org.apache.ibatis.reflection.SystemMetaObject;
import org.apache.ibatis.session.Configuration;
import org.apache.ibatis.session.RowBounds;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.alibaba.druid.sql.SQLUtils;
import com.bicon.base.data.common.StringTools;


/**
 * @ClassName: AutoMapperInterceptor
 * @Description: 对于实现自己的Interceptor而言有两个很重要的注解，
 *               一个是@Intercepts，其值是一个@Signature数组。
 *               @Intercepts用于表明当前的对象是一个Interceptor，
 *               而@Signature则表明要拦截的接口、方法以及对应的参数类型
 * @author cssuger@163.com
 * @date 2017年1月23日 下午1:08:18
 */
@Intercepts({ @Signature(type = StatementHandler.class, method = "prepare", args = { Connection.class,Integer.class }) })
public class AutoMapperInterceptor implements Interceptor {

	private Logger logger = LoggerFactory
			.getLogger(AutoMapperInterceptor.class);

//	protected static final ObjectFactory DEFAULT_OBJECT_FACTORY = new DefaultObjectFactory();
//
//	protected static final ObjectWrapperFactory OBJECT_WRAPPER_FACTORY = new DefaultObjectWrapperFactory();
//
//	protected static final ReflectorFactory REFLECTOR_FACTORY = new DefaultReflectorFactory();
	
	protected Properties properties = new Properties();

	
	
	@Override
	public Object intercept(Invocation invocation)  {
		StatementHandler statementhandler = (StatementHandler) invocation.getTarget();
		MetaObject metaobject = SystemMetaObject.forObject(statementhandler);
		//MetaObject metaobject = MetaObject.forObject(statementhandler, DEFAULT_OBJECT_FACTORY,OBJECT_WRAPPER_FACTORY, REFLECTOR_FACTORY);
		Configuration configuration = (Configuration) metaobject.getValue("delegate.configuration");
		MappedStatement mappedStatement = (MappedStatement) metaobject.getValue("delegate.mappedStatement");
		String classAndMethod = mappedStatement.getId();
		// 获取方法名称，如果方法名称相同了，那么参数类型肯定不同,在mybatis的xml里面select，update，delete，insert
		// 标签的id必须和方法名称一一对应，包括参数类型也必须一一对应
		String methodName = classAndMethod.substring(classAndMethod.lastIndexOf(".") + 1);
		logger.info("调用所在类以及方法名称:"+classAndMethod);
		//Transaction t = Cat.newTransaction("SQL", classAndMethod);
		
		properties = configuration.getVariables();
		BoundSql boundSql = statementhandler.getBoundSql();
		// 该sql有可能没有
		String sql = boundSql.getSql();
		String newsql = null;
		Object parameterObject = boundSql.getParameterObject();
		// 如果没有sql语句的话
		if (StringTools.isBlank(sql)) {
			// 对sql进行拼装
			if (methodName.startsWith("save") || methodName.startsWith("add") || methodName.startsWith("insert")) {
				newsql = SqlBuilder.builderInsert(parameterObject);
			} else if (methodName.startsWith("update") || methodName.startsWith("edit")) {
				newsql = SqlBuilder.builderUpdate(parameterObject);
			} else if (methodName.startsWith("delete") || methodName.startsWith("remove")) {
				newsql = SqlBuilder.builderDelete(parameterObject);
			}
			//Cat.logEvent("SQL.Statement", sql, Message.SUCCESS, sql);
			SqlSource sqlSource = buildSqlSource(configuration, newsql,parameterObject.getClass());
			List<ParameterMapping> parameterMappings = sqlSource.getBoundSql(parameterObject).getParameterMappings();
			metaobject.setValue("delegate.boundSql.sql",SQLUtils.format(sqlSource.getBoundSql(parameterObject).getSql(), properties.getProperty("dbType")));
			metaobject.setValue("delegate.boundSql.parameterMappings", parameterMappings);
			//Cat.logEvent("SQL.Method", newsql, Message.SUCCESS, sql);
		}
//		else{
//			if(methodName.startsWith("page")){
//				if(parameterObject instanceof PageVo){
//					String paginationSql = null;
//					PageVo<?> pageVo = (PageVo<?>)parameterObject;
//					boolean isExport = pageVo.isIsexport();
//					if(isExport) {
//						//说明不分页，需要全部导出
//						paginationSql = sql;
//						//格式化一下sql
//						//String formatSql = SQLUtils.formatMySql(sql);
//					}else {
//						int pageSize = pageVo.getPageSize();
//						int pageNow = pageVo.getPageNow();
//						paginationSql = buliderPagenationsql(sql,pageNow, pageSize);//构建分页sql语句
//						buliderPageCount(pageVo,invocation,metaobject,sql);//计算总条数
//					}
//					List<ParameterMapping> parameterMappings = boundSql.getParameterMappings();
//					 // 禁用内存分页
//					Cat.logEvent("SQL.Statement", sql, Message.SUCCESS, sql);
//					SqlSource sqlSource = buildSqlSource(configuration, paginationSql,parameterObject.getClass());
//					metaobject.setValue("delegate.boundSql.sql",sqlSource.getBoundSql(parameterObject).getSql());
//					metaobject.setValue("delegate.boundSql.parameterMappings", parameterMappings);
//					metaobject.setValue("delegate.rowBounds.offset", RowBounds.NO_ROW_OFFSET);
//					metaobject.setValue("delegate.rowBounds.limit", RowBounds.NO_ROW_LIMIT);
//					Cat.logEvent("SQL.Method", paginationSql, Message.SUCCESS, sql);
//				}
//			}else {
//				Cat.logEvent("SQL.Method", newsql, Message.SUCCESS, sql);
//				Cat.logEvent("SQL.Statement", sql, Message.SUCCESS, sql);
//			}
//			
//		}
		 

		// 调用原始statementHandler的prepare方法
		statementhandler = (StatementHandler) metaobject.getOriginalObject();
		Object retValue = null;
        // 传递给下一个拦截器处理
        //打印sql执行时间
        
		try {
			statementhandler.prepare((Connection) invocation.getArgs()[0],30);
			long start = System.currentTimeMillis();//sql开始执行时间
			retValue = invocation.proceed();
			//t.setStatus(Transaction.SUCCESS);
			//stop.stop();//sql结束执行时间
	        long total = System.currentTimeMillis() - start;
	        if(total > 2000){
	        	String sql1 = (String)metaobject.getValue("delegate.boundSql.sql");
	        	logger.debug("该sql:"+sql1+"花费时间:"+total);
	        }
		} catch (Exception e) {
			// Cat.logError(e);
			e.printStackTrace();
		}finally {
			// t.complete();
		}
        
       // stop.stop();
       
        return retValue;
		//return statementhandler;
	}

	
//	private String buliderPagenationsql(String sql, int pageNow, int pageSize) {
//		return new PagenationsqlFactory().buliderSql(sql, pageNow, pageSize);
//		
//	}
//	
//	
//	protected  class PagenationsqlFactory{
//		public String buliderSql(String sql, int pageNow, int pageSize) {
//			String dirverType = properties.getProperty("dbType"); 
//			if(dirverType.equals(DBType.ORACLE.name)){
//				return   new OracleDialect().buildPaginationSql(sql,pageNow, pageSize);
//			}else if(dirverType.equals(DBType.MYSQL.name)){
//				 return   new MySqlDialect().buildPaginationSql(sql,pageNow, pageSize);
//			}else{
//				throw new RuntimeException("支持该类型的sql");
//			}
//		}
//	}
	
//	/**
//	 * 
//	 * @Title: buliderPageCount   
//	 * @Description: 构建分页总数
//	 * @param: @param pageVo
//	 * @param: @param invocation
//	 * @param: @param metaobject
//	 * @param: @param sql      
//	 * @return: void      
//	 * @throws
//	 */
//	private void buliderPageCount(PageVo<?> pageVo,Invocation invocation,MetaObject metaobject,String sql) {
//		Long rows = 0L;
//		PreparedStatement stmt =null;
//		ResultSet rs = null;
//		try {
//			String countsql = "select count(*) from ("+sql+") as a";
//			//获取数据库连接
//			Connection conn = (Connection)invocation.getArgs()[0];
//			stmt = conn.prepareStatement(countsql);
//			ParameterHandler parameterHandler = (ParameterHandler) metaobject.getValue("delegate.parameterHandler");
//			parameterHandler.setParameters(stmt);
//			rs =	stmt.executeQuery();
//			while(rs.next()) {
//				rows  = rs.getLong(1);
//				break;
//			}
//			pageVo.setRows(rows);
//		}catch(SQLException ex){
//               logger.error("发生异常,异常原因:{}",ExceptionUtils.getStackTrace(ex.fillInStackTrace()));			
//		}finally {
//			try {
//				DbUtils.close(stmt);
//				DbUtils.close(rs);
//			} catch (SQLException e) {
//				e.printStackTrace();
//			}
//		}
//	}
	
	@Override
	public Object plugin(Object target) {
		if (target instanceof StatementHandler) {
			return Plugin.wrap(target, this);
		} else {
			return target;
		}
	}

	@Override
	public void setProperties(Properties properties) {
		//加载第三方Properties 配置文件
		//load();
		//String driver = properties.getProperty("driver");
		
	}
	
//	private void load(){
//		InputStream inputStream = Thread.currentThread().getContextClassLoader().getResourceAsStream("db.properties");
//		try {
//			properties.load(inputStream);
//		} catch (IOException e) {
//			e.printStackTrace();
//		} finally {
//			IOUtils.closeQuietly(inputStream);
//		}
//	}

	protected SqlSource buildSqlSource(Configuration configuration,
			String originalSql, Class<?> parameterType) {
		SqlSourceBuilder builder = new SqlSourceBuilder(configuration);
		return builder.parse(originalSql, parameterType, null);
	}
	
	private SqlSource buildSqlSource(Configuration configuration,String originalSql, Class<?> parameterType,Map<String, Object> additionalParameters) {
		SqlSourceBuilder builder = new SqlSourceBuilder(configuration);
		return builder.parse(originalSql, parameterType, additionalParameters);
	}
	
	static enum DBType{
		MYSQL("MYSQL",1),ORACLE("ORALCE",2);
		
		private int index;
		
		private String name;
		
		// 构造方法  
	    private DBType(String name, int index) {  
	        this.name = name;  
	        this.index = index;  
	    }  
	    // 普通方法  
	    public static String getName(int index) {  
	        for (DBType c : DBType.values()) {  
	            if (c.getIndex() == index) {  
	                return c.name;  
	            }  
	        }  
	        return null;  
	    }  
	    // get set 方法  
	    public String getName() {  
	        return name;  
	    }  
	    public void setName(String name) {  
	        this.name = name;  
	    }  
	    public int getIndex() {  
	        return index;  
	    }  
	    public void setIndex(int index) {  
	        this.index = index;  
	    }  
	}
	
	

}
